function Data(debug) {
	this.SEPARATOR = "|";
	this.requiredVersion = 1;
	this.debug = debug;
	this.type = "local";
	this.db = google.gears.factory.create('beta.database');
	if (!this.initDatabase()) {
		info.error("Local database init failed. Refresh page.");
	} else {
		info.info("Local database init success");
	}
}

Data.prototype.initDatabase = function () {
	try {
		this.db.open('webdb.database');
	} catch (exception) {
		if (this.debug) console.debug("initDatabase(): exception = ", exception.message);
		this.initialized = false;
		return false;
	}
	this.initialized = this.checkSchemaVersion();
	return this.initialized;
};

Data.prototype.checkSchemaVersion = function () {
	var schemaVersion = -1;
	var rs;
	try {
		rs = this.db.execute("SELECT metaVersion from __webdb_schema");
		if (rs.isValidRow()) {
			schemaVersion = Number(rs.field(0));
			if (this.debug) console.debug("initDatabase(): version = ", schemaVersion);
			if (isNaN(schemaVersion)) {
				schemaVersion = -1;
			}
		}
	} catch (exception) {
		if (this.debug) console.debug("initDatabase(): cannot get schema version exception = ", exception.message);
	} finally {
		if (rs) {
			rs.close();
		}
	}
	
	if (this.debug) console.debug("initDatabase(): version = ", schemaVersion);
	var success = false;
	if (this.requiredVersion !== schemaVersion) {
		success = this.updateDatabaseSchema();
	} else {
		if (this.debug) console.debug("initDatabase(): requiredVersion == schemaVersion",
				this.requiredVersion, schemaVersion);
		success = true;
	}
	if (this.debug) console.debug("initDatabase(): success", success);

	return success;
};

Data.prototype.updateDatabaseSchema = function () {
	try {
		this.db.execute("BEGIN");
		var dropQueries = {};
		dropQueries.dropSchema = "DROP TABLE IF EXISTS __webdb_schema ";
		dropQueries.dropSchema = "DROP TABLE IF EXISTS __webdb_history ";
		dropQueries.dropTableList = "DROP TABLE IF EXISTS __webdb_tablelist ";
		
		for (var i in dropQueries) {
			var dropQuery = dropQueries[i];
			if (this.debug) console.debug("updateDatabaseSchema(): ", i);
			this.db.execute(dropQuery);
		}
		
		var createQueries = {};
		createQueries.createSchemaVersion = "CREATE TABLE __webdb_schema (" +
				"metaVersion INTEGER, " +
				"version INTEGER" +
		")";
		createQueries.createHistory = "CREATE TABLE __webdb_history (" +
				"version INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
				"internalId INTEGER, " +
				"sql TEXT NOT NULL, " +
				"params TEXT," +
				"status INTEGER NOT NULL DEFAULT 0, " +
				"time TEXT NOT NULL DEFAULT (strftime('%s','now'))," +
				"metadata TEXT," +
				"metadataParams TEXT," +
				"reverse TEXT," +
				"reverseParams TEXT," +
				"reverseMetadata TEXT," +
				"reverseMetaParams TEXT" +
		")";
		createQueries.createTableList = "CREATE TABLE __webdb_tablelist (" +
				"tablename TEXT PRIMARY KEY NOT NULL, " +
				"tablesql TEXT NOT NULL, " +
				"primaryKey INTEGER" +
		")";
		
		for (var j in createQueries) {
			var createQuery = createQueries[j];
			if (this.debug) console.debug("updateDatabaseSchema(): ", j, createQuery);
			this.db.execute(createQuery);
		}
		this.db.execute("INSERT INTO __webdb_schema (metaVersion, version) VALUES(?, ?)", [this.requiredVersion, 0]); // [metaVersion, version]
		this.db.execute("COMMIT");
	} catch (exception) {
		console.debug(exception.name + " : " + exception.message);
		this.db.execute("ROLLBACK");
		return false;
	}
	return true;
};

Data.prototype.getTableList = function() {
	var tableList = [];
	var rs = this.db.execute("SELECT tablename FROM __webdb_tablelist");
	while (rs.isValidRow()) {
		tableList.push(rs.field(0));
		rs.next();
	}
	rs.close();
	return tableList;
};

Data.prototype.getTableContent = function(tableName) {
	
	var query = "select rowid, * from " + tableName; // FIXME: INTEGER PRIMARY KEY !!!
	var rs;
	var content = {};
	content.rows = [];
	content.fields = [];
	try {
		rs = this.db.execute(query);
		content.fieldCount = rs.fieldCount();
		
		for (var i = 0; i < content.fieldCount; i++) {
			var hidden;
			var fieldName = rs.fieldName(i);
			if (fieldName === "rowid") { // FIXME: INTEGER PRIMARY KEY !!!
//				hidden = false;
				hidden = true;
			} else {
				hidden = false;
			}
			content.fields.push({"name": fieldName, "hidden": hidden});
		}
		
		while (rs.isValidRow()) {
			var row = [];
			for (var j = 0; j < content.fieldCount; j++) {
				row.push(rs.field(j));
			}
			content.rows.push(row);
			rs.next();
		}
	} catch (e) {
		console.error(e);
		content.fieldCount = 0;
	} finally {
		if (rs) {
			rs.close();
		}
	}
	return content;
};

Data.prototype.createTable = function(args) {
	if (!this.isValidTableName(args.tableName)) {
		console.error("Podaj nazwe tabeli");
		return {success: false, msg: "Table name is not correct. Should contain only letters, digits and underscore."};
	}
	
	if (args.columns.length === 0) {
		console.error("Nie podano nazwy zadnego pola");
		return {success: false, msg: "No column name was provided."};
	}
	
	for (var c = 0; c < args.columns.length; c++) {
		if (!this.isValidColumnName(args.columns[c].name)) {
			return {success: false, msg: "Column name '" + args.columns[c].name + "' is not correct. Should contain only letters, digits and underscore."};
		}
	}
	
	var query = "CREATE TABLE ";
	query += args.tableName + " (";
	
	for (var i = 0; i < args.columns.length; i++) {
		var column = args.columns[i];
		query += "`" + column.name + "` " + column.type;
		if (column.pk) {
			query += " PRIMARY KEY";
		}
		
		if (column.unique) {
			query += " UNIQUE";
		}
		if (!column.allowNull) {
			query += " NOT NULL";
		}
		if (i != args.columns.length - 1) {
			query += ", ";
		}
	}
	query += ")";
	console.debug("createTable(): query =", query);
	var metadataQuery = "INSERT INTO __webdb_tablelist (tablename, tablesql) VALUES(?, ?)";
	var metadataParams = JSON.stringify([args.tableName, query]);
	var reverseQuery = "DROP TABLE " + args.tableName + " ";
	var reverseMetadata = "DELETE FROM __webdb_tablelist WHERE tablename = ?";
	var reverseMetaParams = JSON.stringify([args.tableName]);
	console.debug("createTable(): metadataQuery =", metadataQuery);
	console.debug("createTable(): metadataParams =", metadataParams);
	console.debug("createTable(): reverseQuery =", reverseQuery);
	console.debug("createTable(): reverseMetadata =", reverseMetadata);
	console.debug("createTable(): reverseMetaParams =", reverseMetaParams);
	try {
		this.db.execute("BEGIN");
		this.db.execute(query);
		this.db.execute("INSERT INTO __webdb_tablelist (tablename, tablesql) VALUES(?, ?)", [args.tableName, query]);
		this.db.execute("INSERT INTO __webdb_history (sql, metadata, metadataParams, reverse, reverseMetadata, reverseMetaParams) values(?, ?, ?, ?, ?, ?)", [query, metadataQuery, metadataParams, reverseQuery, reverseMetadata, reverseMetaParams]);
		var version = this.db.lastInsertRowId;
		this.db.execute("update __webdb_schema set version = ?", [version]);
		
		this.db.execute("COMMIT");
	} catch (exception) {
		this.db.execute("ROLLBACK");
		console.debug("createTable(): exception", exception);
		return {success: false, msg: exception};
	}
	
	return {success: true};
};

Data.prototype.dropTable = function(tableName) {
	var rs;
	try {
		this.db.execute("BEGIN");
		rs = this.db.execute("SELECT tablesql FROM __webdb_tablelist WHERE tablename = ?", [tableName]);
		var reverse = rs.field(0);
		rs.close();
		var query = "DROP TABLE " + tableName + " ";
		this.db.execute(query);
		var reverseMetadata = "INSERT INTO __webdb_tablelist (tablename, tablesql) VALUES(?, ?)";
		var reverseMetaParams = JSON.stringify([tableName, reverse]);
		var metadataQuery = "DELETE from __webdb_tablelist where tablename = ?";
		var metadataParams = JSON.stringify([tableName]);
		this.db.execute(metadataQuery, [tableName]);
		this.db.execute("INSERT INTO __webdb_history (sql, metadata, metadataParams, reverse, reverseMetadata, reverseMetaParams) VALUES(?, ?, ?, ?, ?, ?)", [query, metadataQuery, metadataParams, reverse, reverseMetadata, reverseMetaParams]);
		var version = this.db.lastInsertRowId;
		this.db.execute("update __webdb_schema set version = ?", [version]);
		
		this.db.execute("COMMIT");
	} catch (e) {
		console.error(e);
		this.db.execute("ROLLBACK");
	} finally {
		if (rs) {
			rs.close();
		}
	}
};

Data.prototype.insertIntoTable = function(tableName, values) {
	
	var query = "INSERT INTO " + tableName + " VALUES(";
	for (var i = 0; i < values.length; i++) {
		if (i != values.length - 1) {
			query += "?, ";
		} else {
			query += "?";
		}
	}
	query += ")";
	
	try {
		this.db.execute("BEGIN");
		console.debug("values", values);
		this.db.execute(query, values);
		var internalId = this.db.lastInsertRowId;
		var params = JSON.stringify(values);
		console.debug("params", params);
		var reverse = "DELETE FROM " + tableName + " WHERE rowid = ?";
		var reverseParams = JSON.stringify([internalId]);
		this.db.execute("INSERT INTO __webdb_history (sql, internalId, params, reverse, reverseParams) VALUES(?, ?, ?, ?, ?)", [query, internalId, params, reverse, reverseParams]);
		var version = this.db.lastInsertRowId;
		this.db.execute("update __webdb_schema set version = ?", [version]);
		this.db.execute("COMMIT");
	} catch (e) {
		console.debug(e);
		this.db.execute("ROLLBACK");
		return {success: false, msg: e};
	}
	return {success: true};
};

Data.prototype.deleteFromTable = function(tableName, rowId) {
	var rs;
	try {
		this.db.execute("BEGIN");
		rs = this.db.execute("SELECT * FROM " + tableName + " WHERE rowid = ?", [rowId]);
		var query = "DELETE FROM " + tableName + " WHERE rowid = ?";
		this.db.execute(query, [rowId]);
		var params = JSON.stringify([rowId]);
		
		var reverseNames = "rowid";
		var reverseValues = "?";
		var values = [rowId];
		for (var i = 0; i < rs.fieldCount(); i++) {
			values.push(rs.field(i));
			reverseNames += ", " + rs.fieldName(i);
			reverseValues += ", ?";
		}
		rs.close();
		var reverse = "INSERT INTO " + tableName + " (" + reverseNames + ") VALUES(" + reverseValues + ")";
		var reverseParams = JSON.stringify(values);
		
		this.db.execute("INSERT INTO __webdb_history (sql, internalId, params, reverse, reverseParams) VALUES(?, ?, ?, ?, ?)", [query, rowId, params, reverse, reverseParams]);
		var version = this.db.lastInsertRowId;
		this.db.execute("update __webdb_schema set version = ?", [version]);
		console.debug("before commit");
		this.db.execute("COMMIT");
	} catch (e) {
		console.error(e);
		this.db.execute("ROLLBACK");
	} finally {
		if (rs) {
			rs.close();
		}
	}
	generateTableContent(tableName);
};

Data.prototype.updateTable = function(tableName, values, fields) {
	var rowid = values.shift(); // ignore rowid edit!!!
	fields.shift();
	var query = "UPDATE " + tableName + " SET ";
	for (var v = 0; v < values.length; v++) { 
		if (v > 0) {
			query += ", ";
		}
		query += fields[v].name + " = ?";
	}
	query += " WHERE rowid = ?";
	console.debug("query =", query);
	values.push(rowid);
	console.debug("values =", values);
	
	var rs;
	try {
		this.db.execute("BEGIN");
		rs = this.db.execute("SELECT * FROM " + tableName + " WHERE rowid = ?", [rowid]);
		var reverse = "UPDATE " + tableName + " SET ";
		var reverseValues = [];
		for (var i = 0; i < rs.fieldCount(); i++) {
			if (i > 0) {
				reverse += ", ";
			}
			reverse += rs.fieldName(i) + " = ?";
			reverseValues.push(rs.field(i));
		}
		reverse += " WHERE rowid = ?";
		console.debug("reverse =", reverse);
		reverseValues.push(rowid);
		console.debug("reverseValues =", reverseValues);
		var reverseParams = JSON.stringify(reverseValues);
		rs.close();
		this.db.execute(query, values);
		var params = JSON.stringify(values);
		console.debug("params = " + params);
		this.db.execute("INSERT INTO __webdb_history (sql, internalId, params, reverse, reverseParams) VALUES(?, ?, ?, ?, ?)", [query, rowid, params, reverse, reverseParams]);
		version = this.db.lastInsertRowId;
		this.db.execute("UPDATE __webdb_schema SET version = ?", [version]);
		this.db.execute("COMMIT");
	} catch (e) {
		console.debug(e);
		this.db.execute("ROLLBACK");
		return {success: false, msg: e};
	} finally {
		if (rs) {
			rs.close();
		}
	}
	return {result: true};
};

Data.prototype.isValidTableName = function(tableName) {
	return tableName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/);
};

Data.prototype.isValidColumnName = function(columnName) {
	return columnName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/);
};
